/*
 * Copyright (c) 2011-2022, baomidou (jobob@qq.com).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baomidou.mybatisplus.extension.conditions.query;

import com.baomidou.mybatisplus.core.conditions.AbstractJoinWrapper;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.enums.BaseFuncEnum;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.extension.conditions.query.interfaces.QueryJoinFunc;

import lombok.AllArgsConstructor;
import lombok.Data;


/**
 * 本类使用BasicJoinQueryWrapper.ModelProperty 来构造多表查询条件
 * 主要搭配myabtis plus advance 和 mybatis x 插件一起使用
 * 普通开发者请使用LambdaJoinQueryWrapper
 * todo 需要优化本类代码
 * demo代码：--如果不手动设置select 则会select user.*,school.*
 *         BasicJoinQueryWrapper<User> wrapper = new BasicJoinQueryWrapper<>(User.class);
 *         wrapper.innerJoin(School.class);
 *         wrapper.eq(new BasicJoinQueryWrapper.ModelProperty(User.class,"schoolId"),1);
 *         wrapper.select(new BasicJoinQueryWrapper.ModelProperty(User.class,"schoolId"));
 *         wrapper.select(new BasicJoinQueryWrapper.ModelProperty(User.class,"userId"));
 *         wrapper.select(new BasicJoinQueryWrapper.ModelProperty(User.class,"name"));
 *         wrapper.select(new BasicJoinQueryWrapper.ModelProperty(School.class,"schoolName"));
 *         wrapper.select(new BasicJoinQueryWrapper.ModelProperty(School.class,"id"));
 *         wrapper.select(new BasicJoinQueryWrapper.ModelProperty(School.class,"remark"));
 *         return
 *                 mapper.selectJoinList(wrapper);
 *
 * @author wanglei
 * @since 2022-03-17
 */
public class BasicJoinQueryWrapper<T> extends AbstractJoinWrapper<T, BasicJoinQueryWrapper.ModelProperty, BasicJoinQueryWrapper<T>> implements QueryJoinFunc<BasicJoinQueryWrapper<T>>, Query<BasicJoinQueryWrapper<T>, T, BasicJoinQueryWrapper.ModelProperty> {

    /**
     * 主表model的class
     */
    private Class<?> mainClass;

    public BasicJoinQueryWrapper(Class<?> mainClass) {
        super(mainClass);
        this.mainClass = mainClass;
        super.initNeed();
    }

    @Override
    protected BasicJoinQueryWrapper<T> instance() {
        return new BasicJoinQueryWrapper<>(mainClass);
    }

    @Override
    public BasicJoinQueryWrapper<T> select(ModelProperty... columns) {
        if (ArrayUtils.isNotEmpty(columns)) {
            for (ModelProperty column : columns) {
                selectColumns.add(SelectColumn.of(column.modelClass, getCache(column.getModelClass(), column.getPropertyName()).getColumn()));
            }
        }
        return (BasicJoinQueryWrapper<T>) typedThis;
    }

    /**
     * 查询函数列,如果column不指定，则查询 *
     * <p>select xx(column) as alias</p>
     */
    public final BasicJoinQueryWrapper<T> selectFun(BaseFuncEnum fun, String alias, ModelProperty column) {
        String columnStr = column == null ? "*" : aliasMap.get(column.modelClass) + Constants.DOT + getCache(column.getModelClass(), column.getPropertyName()).getColumn();
        String funStr = String.format(fun.getSql(), columnStr);
        this.funSqlSelect.add(new SharedString(funStr + Constants.AS + alias));
        return (BasicJoinQueryWrapper<T>) typedThis;
    }

    /**
     * 软删除字段处理
     *
     * @param joinClass
     */
    @Override
    protected void initLogicDelete(Class<?> joinClass, String joinType) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo(joinClass);
        // 不是left join的时候
        if (tableInfo.getLogicDeleteFieldInfo() != null) {
            this.joinFroms.append(Constants.AND + Constants.SPACE).append(tableInfo.getLogicDeleteFieldInfo().getColumn()).append(Constants.EQUALS).append(parseLogicNotDeleteValue(tableInfo.getLogicDeleteFieldInfo()));
        }
        //如果没有join其他的表，需要把主表的软删除字段放到on上
        if (this.aliasMap.size() == 2) {
            tableInfo = TableInfoHelper.getTableInfo(this.mainClass);
            if (tableInfo.getLogicDeleteFieldInfo() != null) {
                this.joinFroms.append(Constants.AND + Constants.SPACE).append(tableInfo.getLogicDeleteFieldInfo().getColumn())
                    .append(Constants.EQUALS).append(parseLogicNotDeleteValue(tableInfo.getLogicDeleteFieldInfo()));
            }
        }
    }

    @Override
    public MergeSegments getExpression() {
        //如果没有join其他的表，需要把主表的软删除字段放到where上
        if (this.aliasMap.size() == 1 && !isAppendMainLogicDelete) {
            isAppendMainLogicDelete = true;
            TableInfo tableInfo = TableInfoHelper.getTableInfo(this.mainClass);
            if (tableInfo.getLogicDeleteFieldInfo() != null) {
                eq(new ModelProperty(mainClass, tableInfo.getLogicDeleteFieldInfo().getProperty()), parseLogicNotDeleteValue(tableInfo.getLogicDeleteFieldInfo()));
            }
        }
        return expression;
    }

    @Override
    public BasicJoinQueryWrapper<T> join(Class<?> joinClass, String joinType) {
        pubJoin(joinClass, null, null, Constants.LEFT_JOIN);
        return this;
    }

    @Override
    protected String columnToString(ModelProperty column) {
        if (!super.aliasMap.containsKey(column.getModelClass())) {
            throw ExceptionUtils.mpe("not join class: \"%s\".", column.getModelClass().getName());
        }
        return super.aliasMap.get(column.getModelClass()) + "." + getCache(column.modelClass, column.getPropertyName()).getColumn();
    }

    /**
     * 查询/过滤条件字段
     */
    @Data
    @AllArgsConstructor
    public static class ModelProperty {

        /**
         * 类名
         */
        private Class<?> modelClass;

        /**
         * 属性名
         */
        private String propertyName;
    }
}
